/**
 * 数组转树形结构
 @param {Array} array
 @param {number} parentId
 *@return {Array} 树形结构
 * */
function arrayToTree(array, parentId = 0) {
  const tree = [];
  const map = {};

  array.forEach(item => {
    const newItem = {...item};
    map[newItem.id] = newItem;

    if (newItem.parentId === parentId) {
      tree.push(newItem);
    } else {
      const parent = map[newItem.parentId];
      if (!parent.children) {
        parent.children = [];
      }
      parent.children.push(newItem);
    }
  });

  return tree;
}

/**
 * 树形结构转数组结构
 * @param {Array} array
 * @param {newArray} newArray
 * @return newArray 新数组
 * */
function treeToArray(array, newArray = []) {
  array.forEach(item => {
    newArray.push(item)
    if (item.children) {
      const {children} = item
      delete item.children
      treeToArray(children, newArray)
    }
  })
  return newArray
}

/**
 * @param {Array} list - 原始树形数据
 * @param {Object} keyMap - 要增加的新key及其对应的原始key
 * @returns {Array} - 新的树形数据
 */

function changeTreeKey(list, keyMap) {
  return list.map((item) => {
    // 动态遍历 keyMap 来给 item 添加新键值
    Object.keys(keyMap).forEach(newKey => {
      const oldKey = keyMap[newKey];
      item[newKey] = item[oldKey]; // 添加新的键和值
    });

    // 如果有 children 则递归处理
    if (item.children) {
      item.children = changeTreeKey(item.children, keyMap);
    }

    return item;
  });
}

/**
 * 防抖函数
 * @param {Function} func - 要防抖的函数
 * @param {number} wait - 防抖时间，单位毫秒
 * @returns {Function} - 防抖后的函数
 * */
function debounce(func, wait) {
  let timer = null;
  return function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, arguments);
    }, wait);
  }
}

/**
 * @param {Function} func - 要节流的函数
 * @param {number} wait - 节流时间，单位毫秒
 * @returns {Function} - 节流后的函数
 * */
function throttle(func, wait) {
  let timer = null;
  return function () {
    if (timer) return
    timer = setTimeout(() => {
      func.apply(this, arguments);
      timer = null
    })
  }
}

/**
 * 替换搜索关键字
 * @param {String} result - 要匹配的字符串
 * @param {String} keyword - 要匹配的关键字
 * @returns {String} - 匹配后的字符串
 * */
function replaceSearchKeyword(result, keyword) {
  const Reg = new RegExp(keyword, 'ig')
  let res = ''
  if (result) {
    res = result.replace(Reg, `<span style="color: #507DAF;">$&</span>`)
    return res
  }
  return result
}

/***
 * 复制文本到剪贴板
 * @param {String} text - 要复制的文本
 * @returns {Promise} - 复制成功后的Promise
 * */
function copyToClip(text) {
  return new Promise((resolve, reject) => {
    try {
      const input = document.createElement('textarea')
      input.value = text
      input.setAttribute('readonly', 'readonly')
      document.body.appendChild(input)
      input.select()
      if (document.execCommand('copy')) {
        document.execCommand('copy')
        document.body.removeChild(input)
        resolve(text)
      }
    } catch (error) {
      reject(error)
    }
  })
}

/**
 * 判断是否是微信浏览器
 * @returns {boolean}
 * */
function isWeChatBrowser() {
  var userAgent = window.navigator.userAgent.toLowerCase()
  return userAgent.match(/micromessenger/i) !== null
}

/**
 * 判断是否是移动端
 * @returns {boolean}
 * */
function isMobile() {
  // 判断是否是移动设备的正则表达式
  const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;

  // 获取用户代理字符串
  const userAgent = navigator.userAgent;

  // 使用正则表达式进行匹配
  return mobileRegex.test(userAgent);
}

/**
 * 判断是否为空
 * @param {*} value
 * @returns {boolean}
 * */
function isNotEmpty(value) {
  return value !== null && value !== undefined && value !== '';
}

/**
 * 过滤空属性
 * @param {Object} obj - 要过滤的对象
 * @returns {Object} - 过滤后的对象
 * */
function filterEmptyProperties(obj) {
  const filteredObj = {};
  Object.keys(obj).forEach(key => {
    if (obj[key] !== null && obj[key] !== undefined && obj[key] !== '') {
      filteredObj[key] = obj[key];
    }
  });
  return filteredObj;
}

/**
 * 根据id查找节点
 * @param {Array} data - 树形数据
 * @param {number} id - 要查找的节点id
 * @returns {Object|null} - 找到的节点对象或null
 * */
function findNodeById(data, id) {
  // 辅助函数，递归搜索节点
  function search(nodes, id) {
    for (const node of nodes) {
      if (node.id === id) {
        return node; // 找到节点，返回节点对象
      }
      if (node.children) {
        const result = search(node.children, id); // 递归搜索子节点
        if (result) {
          return result; // 如果找到了，直接返回结果
        }
      }
    }
    return null; // 如果未找到节点，返回 null
  }

  // 在整个数据中查找
  for (const item of data) {
    const result = search([item], id); // 从顶层节点开始搜索
    if (result) {
      return result; // 如果找到了，直接返回结果
    }
  }

  return null; // 如果未找到节点，返回 null
}

/**
 * 获取URL参数
 * @param {String} url - 要获取参数的URL
 * @returns {Object} - 包含参数的对象
 * */
function getURLParams(url) {
  const params = {};
  const urlParts = url.split('?');

  if (urlParts.length > 1) {
    const queryString = urlParts[1];
    const keyValuePairs = queryString.split('&');
    keyValuePairs.forEach(pair => {
      const keyValue = pair.split('=');
      const key = decodeURIComponent(keyValue[0]);
      const value = decodeURIComponent(keyValue[1] || '');
      params[key] = value;
    });
  }

  return params;
}
/**
 * 加载脚本
 * @param {String} src - 脚本的URL
 * @param {Function} callback - 加载完成后的回调函数
 * */
function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;
  script.onload = callback;
  document.head.appendChild(script);
}


/*
* * 浮点数运算加法
* @param {Number} num1
 * @param {Number} num2
 * @returns {Number}
* **/
function add(num1, num2) {
  let r1, r2, m;
  try {
    r1 = num1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = num2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  return (num1 * m + num2 * m) / m;
}

/**

 * 浮点数运算减法
 * @param {Number} num1
 * @param {Number} num2
 * @returns {Number}
 * */
function subtract(num1, num2) {
  let r1, r2, m, n;
  try {
    r1 = num1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = num2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2)); // 获取最大小数位数的10的幂
  n = (r1 >= r2) ? r1 : r2;           // 计算保留的小数位数
  return ((num1 * m - num2 * m) / m).toFixed(n); // 返回保留n位小数的结果
}

/**
 *  浮点数运算乘法
 * @param {Number} num1
 * @param {Number} num2
 * @returns {Number}
 * */
function multiply(num1, num2) {
  let m = 0,
      s1 = num1.toString(),
      s2 = num2.toString();
  try {
    m += s1.split(".")[1].length;
  } catch (e) {}
  try {
    m += s2.split(".")[1].length;
  } catch (e) {}
  return (
      (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
      Math.pow(10, m)
  );
}

/**
 * 浮点数运算除法
 * @param {Number} num1
 * @param {Number} num2
 * @returns {Number}
 * */
function divide(num1, num2) {
  let t1 = 0,
      t2 = 0,
      r1,
      r2;
  try {
    t1 = num1.toString().split(".")[1].length;
  } catch (e) {}
  try {
    t2 = num2.toString().split(".")[1].length;
  } catch (e) {}
  r1 = Number(num1.toString().replace(".", ""));
  r2 = Number(num2.toString().replace(".", ""));
  return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
 * @description 格式化时间
 * @param {String|Number} dateTime 需要格式化的时间戳
 * @param {String} formatStr 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
 * @returns {string} 返回格式化后的字符串
 */
function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
  let date
  // 若传入时间为假值，则取当前时间
  if (!dateTime) {
    date = new Date()
  }
  // 若为unix秒时间戳，则转为毫秒时间戳（逻辑有点奇怪，但不敢改，以保证历史兼容）
  else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
    date = new Date(dateTime * 1000)
  }
  // 若用户传入字符串格式时间戳，new Date无法解析，需做兼容
  else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
    date = new Date(Number(dateTime))
  }
      // 处理平台性差异，在Safari/Webkit中，new Date仅支持/作为分割符的字符串时间
  // 处理 '2022-07-10 01:02:03'，跳过 '2022-07-10T01:02:03'
  else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
    date = new Date(dateTime.replace(/-/g, '/'))
  }
  // 其他都认为符合 RFC 2822 规范
  else {
    date = new Date(dateTime)
  }

  const timeSource = {
    'y': date.getFullYear().toString(), // 年
    'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
    'd': date.getDate().toString().padStart(2, '0'), // 日
    'h': date.getHours().toString().padStart(2, '0'), // 时
    'M': date.getMinutes().toString().padStart(2, '0'), // 分
    's': date.getSeconds().toString().padStart(2, '0') // 秒
    // 有其他格式化字符需求可以继续添加，必须转化成字符串
  }

  for (const key in timeSource) {
    const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
    if (ret) {
      // 年可能只需展示两位
      const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
      formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
    }
  }

  return formatStr
}

/**
 * @description 时间戳转为多久之前
 * @param {String|Number} timestamp 时间戳
 * @param {String|Boolean} format
 * 格式化规则如果为时间格式字符串，超出一定时间范围，返回固定的时间格式；
 * 如果为布尔值false，无论什么时间，都返回多久以前的格式
 * @returns {string} 转化后的内容
 */
function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
  if (timestamp == null) timestamp = Number(new Date())
  timestamp = parseInt(timestamp)
  // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
  if (timestamp.toString().length === 10) timestamp *= 1000
  let timer = (new Date()).getTime() - timestamp
  timer = parseInt(timer / 1000)
  // 如果小于5分钟,则返回"刚刚",其他以此类推
  let tips = ''
  switch (true) {
    case timer < 300:
      tips = '刚刚'
      break
    case timer >= 300 && timer < 3600:
      tips = `${parseInt(timer / 60)}分钟前`
      break
    case timer >= 3600 && timer < 86400:
      tips = `${parseInt(timer / 3600)}小时前`
      break
    case timer >= 86400 && timer < 2592000:
      tips = `${parseInt(timer / 86400)}天前`
      break
    default:
      // 如果format为false，则无论什么时间戳，都显示xx之前
      if (format === false) {
        if (timer >= 2592000 && timer < 365 * 86400) {
          tips = `${parseInt(timer / (86400 * 30))}个月前`
        } else {
          tips = `${parseInt(timer / (86400 * 365))}年前`
        }
      } else {
        tips = timeFormat(timestamp, format)
      }
  }
  return tips
}

/**
 * @description 去除空格
 * @param {string} str 需要去除空格的字符串
 * @param {string} pos both(左右)|left|right|all 默认both
 */
function trim(str, pos = 'both') {
  str = String(str)
  if (pos === 'both') {
    return str.replace(/^\s+|\s+$/g, '')
  }
  if (pos === 'left') {
    return str.replace(/^\s*/, '')
  }
  if (pos === 'right') {
    return str.replace(/(\s*$)/g, '')
  }
  if (pos === 'all') {
    return str.replace(/\s+/g, '')
  }
  return str
}
/**
 * @description 深度克隆
 * @param {object} obj 需要深度克隆的对象
 * @param cache 缓存
 * @returns {*} 克隆后的对象或者原值（不是对象）
 */
function deepClone(obj, cache = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (cache.has(obj)) return cache.get(obj);
  let clone;
  if (obj instanceof Date) {
    clone = new Date(obj.getTime());
  } else if (obj instanceof RegExp) {
    clone = new RegExp(obj);
  } else if (obj instanceof Map) {
    clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
  } else if (obj instanceof Set) {
    clone = new Set(Array.from(obj, value => deepClone(value, cache)));
  } else if (Array.isArray(obj)) {
    clone = obj.map(value => deepClone(value, cache));
  } else if (Object.prototype.toString.call(obj) === '[object Object]') {
    clone = Object.create(Object.getPrototypeOf(obj));
    cache.set(obj, clone);
    for (const [key, value] of Object.entries(obj)) {
      clone[key] = deepClone(value, cache);
    }
  } else {
    clone = Object.assign({}, obj);
  }
  cache.set(obj, clone);
  return clone;
}


/**
 * @description JS对象深度合并
 * @param {object} target 需要拷贝的对象
 * @param {object} source 拷贝的来源对象
 * @returns {object|boolean} 深度合并后的对象或者false（入参有不是对象）
 */
function deepMerge(target = {}, source = {}) {
  target = deepClone(target)
  if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
  const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
  for (const prop in source) {
    if (!source.hasOwnProperty(prop)) continue;
    const sourceValue = source[prop];
    const targetValue = merged[prop];
    if (sourceValue instanceof Date) {
      merged[prop] = new Date(sourceValue);
    } else if (sourceValue instanceof RegExp) {
      merged[prop] = new RegExp(sourceValue);
    } else if (sourceValue instanceof Map) {
      merged[prop] = new Map(sourceValue);
    } else if (sourceValue instanceof Set) {
      merged[prop] = new Set(sourceValue);
    } else if (typeof sourceValue === 'object' && sourceValue !== null) {
      merged[prop] = deepMerge(targetValue, sourceValue);
    } else {
      merged[prop] = sourceValue;
    }
  }
  return merged;
}

/**
 * @description 判断点是否在多边形内
 * @param {object} point 点坐标 {x: 10, y: 20}
 * @param {array} polygon 多边形坐标 [{x: 10, y: 20}, {x: 30, y: 40}, {x: 50, y: 60}]
 * @returns {boolean} 是否在多边形内
 */
function isPointInPolygon(point, polygon) {
  let isInside = false;
  const {x, y} = point;
  const n = polygon.length;

  for (let i = 0, j = n - 1; i < n; j = i++) {
    const xi = polygon[i].x, yi = polygon[i].y;
    const xj = polygon[j].x, yj = polygon[j].y;

    const intersect = ((yi > y) !== (yj > y)) &&
        (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
    if (intersect) isInside = !isInside;
  }
  return isInside;
}



export {
  arrayToTree,
  treeToArray,
  changeTreeKey,
  debounce,
  throttle,
  replaceSearchKeyword,
  isWeChatBrowser,
  isMobile,
  isNotEmpty,
  filterEmptyProperties,
  findNodeById,
  getURLParams,
  loadScript,
  add,
  subtract,
  multiply,
  divide,
  timeFormat,
  timeFrom,
  trim,
  deepClone,
  deepMerge,
  isPointInPolygon,
  copyToClip
}
